package core

import (
	"errors"
	"fmt"
	"gitee.com/hongzhaomin/gspring/annotation"
	"gitee.com/hongzhaomin/gspring/aware"
	"gitee.com/hongzhaomin/gspring/consts"
	"gitee.com/hongzhaomin/gspring/enums"
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/gspring/lock"
	"gitee.com/hongzhaomin/gspring/tools"
	"reflect"
)

var _ iface.ListableBeanFactory = (*AbstractBeanFactory)(nil)
var _ iface.BeanDefinitionRegistry = (*AbstractBeanFactory)(nil)

type AbstractBeanFactory struct {
	*DefaultSingletonBeanRegistry

	// 所有bean定义信息列表
	beanName2DefinitionMap map[string]iface.BeanDefinition

	// beanDefinition相关可重入锁
	beanDefinitionPoolReentrantLock *lock.ReentrantMutex

	// gspring容器中bean的名称切片
	beanDefinitionNames []string

	// map.key为反射类型的非指针类型
	beanNamesByType map[reflect.Type][]string

	// BeanPostProcessor增强器切片
	beanPostProcessors []iface.BeanPostProcessor
}

func NewAbstractBeanFactory() *AbstractBeanFactory {
	return &AbstractBeanFactory{
		DefaultSingletonBeanRegistry:    NewDefaultSingletonBeanRegistry(),
		beanDefinitionPoolReentrantLock: new(lock.ReentrantMutex),
		beanName2DefinitionMap:          make(map[string]iface.BeanDefinition, 256),
		beanDefinitionNames:             make([]string, 0, 256),
		beanNamesByType:                 make(map[reflect.Type][]string, 256),
		beanPostProcessors:              make([]iface.BeanPostProcessor, 0, 256),
	}
}

func (my *AbstractBeanFactory) GetBeanByName(beanName string) any {
	return my.GetBean(beanName, nil)
}

func (my *AbstractBeanFactory) GetBean(beanName string, refType reflect.Type) any {
	err, singleton := my.doGetBean(beanName, refType)
	if err != nil {
		panic(err)
	}
	return singleton
}

func (my *AbstractBeanFactory) GetBeanByType(beanType any) any {
	return my.GetBeanByRt(reflect.TypeOf(beanType))
}

func (my *AbstractBeanFactory) GetBeanByRt(refType reflect.Type) any {
	fullPathName := tools.GetFullPathNameByRt(refType)
	beanNames := my.GetBeanNamesForType(refType)
	if len(beanNames) == 0 {
		panic(errors.New(fmt.Sprintf("bean not found: '%s'", fullPathName)))
	} else if len(beanNames) > 1 {
		panic(errors.New(fmt.Sprintf("required a single bean, but 2 were found: '%s'", fullPathName)))
	}
	return my.GetBeanByName(beanNames[0])
}

// GetBeans 如果没有，返回空数组，不报错
func (my *AbstractBeanFactory) GetBeans(refType reflect.Type) []any {
	beanNames := my.GetBeanNamesForType(refType)
	beans := make([]any, 0, len(beanNames))
	for _, beanName := range beanNames {
		beans = append(beans, my.GetBeanByName(beanName))
	}
	return beans
}

func (my *AbstractBeanFactory) doGetBean(beanName string, rt reflect.Type) (error, any) {
	tools.Assert().NotBlank(beanName, "doGetBean方法beanName参数不能为空")
	singleton := my.GetSingleton(beanName)
	if singleton == nil {
		bd, ok := my.beanName2DefinitionMap[beanName]
		if !ok {
			return errors.New(fmt.Sprintf("找不到bean定义信息: %s", beanName)), nil
		}

		singleton = my.GetSingletonOrCreate(beanName, func() (err error, bean any) {
			defer func() {
				recoverErr := recover()
				if recoverErr != nil {
					switch errMsg := recoverErr.(type) {
					case string:
						err = errors.New(errMsg)
					case error:
						err = errMsg
					default:
						err = errors.New("未知错误")
					}
				}
			}()
			return my.createBean(beanName, bd)
		})
	}

	if rt != nil && !tools.Assignable(singleton, rt) {
		return errors.New(fmt.Sprintf("bean [%s] 不能赋值给类型 [%s]", beanName, rt.Name())), nil
	}
	return nil, singleton
}

func (my *AbstractBeanFactory) createBean(beanName string, bd iface.BeanDefinition) (error, any) {
	bean := my.doCreateBean(bd)
	if bean == nil {
		return errors.New("根据bean定义信息创建的bean实例值为nil"), nil
	}

	if my.IsSingletonCurrentlyInCreation(beanName) {
		// bean正在被创建，放入早期bean容器中
		bean = my.AddEarlySingleton(beanName, bean)
	}

	// 属性赋值
	my.populateBean(beanName, bean, bd)

	// Aware回调
	my.invokeAwareMethods(beanName, bean)

	// BeanPostProcessor
	wrappedBean := bean
	wrappedBean = my.applyPostProcessBeforeInitialization(beanName, bean)

	// init初始化
	my.invokeInitMethods(beanName, wrappedBean, bd)

	// BeanPostProcessor
	wrappedBean = my.applyPostProcessAfterInitialization(beanName, wrappedBean)

	return nil, wrappedBean
}

func (my *AbstractBeanFactory) doCreateBean(bd iface.BeanDefinition) any {
	// 1、直接获取bd中的bean
	//if bd.GetObject() != nil {
	//	return bd.GetObject()
	//}

	// 2、根据bd中的反射类型，通过反射获取bean对象
	methodInfo := bd.GetCreateBeanMethodInfo()
	if methodInfo == nil {
		return reflect.New(bd.GetObjRefType()).Interface()
	}

	// 3、根据bd中的方法，反射执行，获取bean对象 -> 配置类 @Bean
	var rvReturnedBean reflect.Value
	rtParentBean := bd.GetParent().GetObjRefType()

	// 获取方法调用的第一个入参：receiver
	// 这里使用receiver入参的方式调用方法的目的是：
	// 假如结构体需要注入其他对象，而执行方法创建对象时需要这些属性
	// 如果用为注入属性的bean的reflect.Value解析的方法执行时，则可能导致属性值缺失
	var rvReceiver reflect.Value
	rvReceiver = reflect.ValueOf(my.GetBeanByName(bd.GetParent().GetBeanName()))
	if methodInfo.ReceiverNotPtr {
		rvReceiver = reflect.Indirect(rvReceiver)
	}

	paramInNum := len(methodInfo.RtDeclaredParamIns)
	if paramInNum == 0 {
		// 执行无参方法
		rvReturnedBean = methodInfo.RvMethod.Call([]reflect.Value{rvReceiver})[0]
	} else {
		// 收集方法参数
		rvParamIns := make([]reflect.Value, 0, paramInNum+1)
		// 添加第一个入参receiver
		rvParamIns = append(rvParamIns, rvReceiver)
		for _, rtIn := range methodInfo.RtDeclaredParamIns {
			in := my.GetBeanByRt(rtIn)
			rvParamIns = append(rvParamIns, reflect.ValueOf(in))
		}
		// 执行有参方法
		rvReturnedBean = methodInfo.RvMethod.Call(rvParamIns)[0]
	}

	tools.Assert().IsTrue(rvReturnedBean.IsValid(), fmt.Sprintf("%s method [%s] returned value must not be <nil> value.",
		tools.GetFullPathNameByRt(rtParentBean), methodInfo.MethodName))
	return my.checkAndWrapperReturnedBean(methodInfo, rvReturnedBean.Interface())
}

func (my *AbstractBeanFactory) checkAndWrapperReturnedBean(methodInfo *iface.CreateBeanMethodInfo, returnedBean any) any {
	rv := reflect.ValueOf(returnedBean)
	if rv.Kind() == reflect.Ptr {
		// 指针则直接返回
		return returnedBean
	}

	// 不是指针，就创建一个新的结构体指针，赋值后返回
	rvPtrNew := reflect.New(rv.Type())
	rvPtrNew.Elem().Set(rv)
	wrapperBean := rvPtrNew.Interface()
	tools.Assert().IsTrue(tools.Assignable(wrapperBean, methodInfo.RtDeclaredReturnBean),
		"@Bean定义的方法返回值必须被定义为可接收结构体指针的类型")
	return wrapperBean
}

func (my *AbstractBeanFactory) GetBeanNamesForType(refType reflect.Type) []string {

	refType = tools.ReflectTypeIndirect(refType)
	switch refType.Kind() {
	case reflect.Struct:
		return my.doGetBeanNamesForType(refType)
	case reflect.Interface:
		// 接口类型则放入缓存
		if cache, ok := my.beanNamesByType[refType]; ok {
			return cache
		}

		beanNames := my.doGetBeanNamesForType(refType)
		if len(beanNames) > 0 {
			my.beanNamesByType[refType] = beanNames
		}
		return beanNames
	default:
		panic(errors.New(fmt.Sprintf("无效的类型: %s", refType.String())))
	}
}

func (my *AbstractBeanFactory) doGetBeanNamesForType(rt reflect.Type) []string {
	beanNames := make([]string, 0, 1)
	for name, bd := range my.beanName2DefinitionMap {
		singleton := my.GetSingleton(name)
		if singleton != nil && tools.Assignable(singleton, rt) {
			beanNames = append(beanNames, name)
			continue
		}
		if tools.Assignable4Rt(bd.GetObjRefTypeToPtr(), rt) {
			beanNames = append(beanNames, name)
		}
	}
	return beanNames
}

func (my *AbstractBeanFactory) AddBeanPostProcessor(beanPostProcessor iface.BeanPostProcessor) {
	tools.Assert().NotNil(beanPostProcessor, "BeanPostProcessor must not be nil")
	// 移除旧的
	tools.RemoveFromSlice(&my.beanPostProcessors, beanPostProcessor)
	// 添加新的
	my.beanPostProcessors = append(my.beanPostProcessors, beanPostProcessor)
}

func (my *AbstractBeanFactory) AddBeanPostProcessors(beanPostProcessors []iface.BeanPostProcessor) {
	// 移除旧的
	tools.RemoveAllFromSlice(&my.beanPostProcessors, beanPostProcessors)
	// 添加新的
	my.beanPostProcessors = append(my.beanPostProcessors, beanPostProcessors...)
}

func (my *AbstractBeanFactory) getBeanPostProcessors() []iface.BeanPostProcessor {
	return my.beanPostProcessors
}

func (my *AbstractBeanFactory) getInstantiationAwares() []iface.InstantiationAwareBeanPostProcessor {
	instantiationAwares := make([]iface.InstantiationAwareBeanPostProcessor, 0, 16)
	for _, postProcessor := range my.beanPostProcessors {
		if instantiationAware, ok := postProcessor.(iface.InstantiationAwareBeanPostProcessor); ok {
			instantiationAwares = append(instantiationAwares, instantiationAware)
		}
	}
	return instantiationAwares
}

func (my *AbstractBeanFactory) invokeAwareMethods(beanName string, bean any) {
	switch bean.(type) {
	case aware.BeanNameAware:
		bean.(aware.BeanNameAware).SetBeanName(beanName)
	case aware.BeanFactoryAware:
		bean.(aware.BeanFactoryAware).SetBeanFactory(my)
	}
}

func (my *AbstractBeanFactory) applyPostProcessBeforeInitialization(beanName string, bean any) any {
	result := bean
	for _, beanPostProcessor := range my.getBeanPostProcessors() {
		currentBean := beanPostProcessor.PostProcessBeforeInitialization(beanName, bean)
		if currentBean == nil {
			return result
		}
		result = currentBean
	}
	return result
}

func (my *AbstractBeanFactory) applyPostProcessAfterInitialization(beanName string, bean any) any {
	result := bean
	for _, beanPostProcessor := range my.getBeanPostProcessors() {
		currentBean := beanPostProcessor.PostProcessAfterInitialization(beanName, bean)
		if currentBean == nil {
			return result
		}
		result = currentBean
	}
	return result
}

func (my *AbstractBeanFactory) invokeInitMethods(beanName string, bean any, bd iface.BeanDefinition) {
	// 优先第一种方式：使用 [annotation.Initialize] 注解的方式，实现 Init() 方法
	if initialize, ok := bean.(annotation.Initialize); ok {
		initialize.Init()
		return
	}
	// 其次第二种方式：
	beanKind := enums.GetBeanKindEnum(bean)
	if beanKind.IsValid() {
		initMethodName, ok := tools.FindAnnotationValueByType(bd.GetObjRefType(),
			beanKind.GetBeanType(), consts.GspringBeanInitMethodTag)
		if !ok {
			// 没有初始化方法，无需执行
			return
		}

		fullPathName := tools.GetFullPathName(bean)
		if initMethodName == "" {
			// 定义了[initMethod]Tag，但是未指定初始化方法名称，panic 终止容器启动
			panic(fmt.Errorf("tag[%s] not define init method name in struct[%s]",
				consts.GspringBeanNameTag, fullPathName))
		}

		isPtrImplement := false
		rt := tools.IndirectReflectType(bean)
		initMethod, exist := rt.MethodByName(initMethodName)
		if !exist {
			// 结构体没有找到此方法，使用结构体指针再找一次
			initMethod, exist = reflect.PointerTo(rt).MethodByName(initMethodName)
			if !exist {
				// 指定了初始化方法，但找不到这个方法，那就panic，终止容器启动
				panic(fmt.Errorf("init method [%s] not exist in struct[%s]", initMethodName, fullPathName))
			}
			isPtrImplement = true
		}
		if !initMethod.IsExported() {
			// 指定的初始化方法，是非导出的，panic 终止容器启动
			panic(fmt.Errorf("init method [%s] must be exported in struct[%s]", initMethodName, fullPathName))
		}

		rtMethod := initMethod.Type
		if rtMethod.NumIn() > 1 { // 所有方法第一个入参是持有该方法的对象
			// 指定的初始化方法应该是无入参的，否则panic 终止容器启动
			panic(fmt.Errorf("init method [%s] must be no params in struct[%s]", initMethodName, fullPathName))
		}
		if rtMethod.NumOut() > 0 {
			// 指定的初始化方法应该是无返回值的，否则panic 终止容器启动
			panic(fmt.Errorf("init method [%s] must be no returns in struct[%s]", initMethodName, fullPathName))
		}

		// 执行初始化方法
		if isPtrImplement {
			reflect.ValueOf(bean).MethodByName(initMethodName).Call([]reflect.Value{})
		} else {
			reflect.ValueOf(bean).Elem().MethodByName(initMethodName).Call([]reflect.Value{})
		}
	}
}

func (my *AbstractBeanFactory) populateBean(beanName string, bean any, bd iface.BeanDefinition) {
	for _, instantiationAware := range my.getInstantiationAwares() {
		instantiationAware.PostProcessProperties(beanName, bean)
	}
}

func (my *AbstractBeanFactory) RegisterBeanDefinition(beanName string, bd iface.BeanDefinition) {
	tools.Assert().NotBlank(beanName, "beanName must not be blank")
	tools.Assert().NotNil(bd, "beanDefinition must not be <nil>")
	my.beanDefinitionPoolReentrantLock.Lock()
	defer my.beanDefinitionPoolReentrantLock.Unlock()
	tools.Assert().IsFalse(my.ContainsLocalBean(beanName), "bean can not register twice: "+beanName)
	my.beanName2DefinitionMap[beanName] = bd
	my.beanDefinitionNames = append(my.beanDefinitionNames, beanName)
}

func (my *AbstractBeanFactory) RemoveBeanDefinition(beanName string) {
	if beanName != "" {
		my.beanDefinitionPoolReentrantLock.Lock()
		defer my.beanDefinitionPoolReentrantLock.Unlock()
		delete(my.beanName2DefinitionMap, beanName)
	}
}

func (my *AbstractBeanFactory) GetBeanDefinition(beanName string) (error, iface.BeanDefinition) {
	bd, ok := my.beanName2DefinitionMap[beanName]
	if !ok {
		return errors.New(fmt.Sprintf("beanDefinition is not exist: %s", beanName)), nil
	}
	return nil, bd
}

func (my *AbstractBeanFactory) GetBeanDefinitionNames() []string {
	return my.beanDefinitionNames
}

func (my *AbstractBeanFactory) ContainsBeanDefinition(beanName string) bool {
	if beanName == "" {
		return false
	}
	_, ok := my.beanName2DefinitionMap[beanName]
	return ok
}

func (my *AbstractBeanFactory) ContainsLocalBean(beanName string) bool {
	return my.ContainsSingleton(beanName) || my.ContainsBeanDefinition(beanName)
}

func (my *AbstractBeanFactory) GetBeanDefinitionCount() int {
	return len(my.beanName2DefinitionMap)
}

func (my *AbstractBeanFactory) RegisterSingleton(beanName string, singletonBean any) {
	my.RegisterBeanDefinition(beanName, NewDefaultBeanDefinition(beanName, reflect.TypeOf(singletonBean)))
	my.DefaultSingletonBeanRegistry.RegisterSingleton(beanName, singletonBean)
}

func (my *AbstractBeanFactory) DestroySingletons() {
	my.DefaultSingletonBeanRegistry.DestroySingletons()

	my.beanName2DefinitionMap = make(map[string]iface.BeanDefinition)
	my.beanDefinitionNames = make([]string, 0)
	my.beanNamesByType = make(map[reflect.Type][]string)
	my.beanPostProcessors = make([]iface.BeanPostProcessor, 0)
}
